﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GRIP;
using TUIO;

namespace GRIP.Gestures
{
    public class KinectHandConstructor : GestureConstructor
    {
        public KinectHandConstructor(HandCardinality handType, KinectAdapter kinect)
        {
            if (handType == HandCardinality.Left)
            {
                ThumbContact = kinect.ThumbLeft;
                IndexContact = kinect.IndexFingerLeft;
                MiddleContact = kinect.MiddleFingerLeft;
                RingContact = kinect.RingFingerLeft;
                PinkyContact = kinect.PinkyLeft;
                HandContact = kinect.HandLeft;
                WristContact = kinect.WristLeft;
                Cardinality = GestureCardinality.Left;
            }
            else
            {
                ThumbContact = kinect.ThumbRight;
                IndexContact = kinect.IndexFingerRight;
                MiddleContact = kinect.MiddleFingerRight;
                RingContact = kinect.RingFingerRight;
                PinkyContact = kinect.PinkyRight;
                HandContact = kinect.HandRight;
                WristContact = kinect.WristRight;
                Cardinality = GestureCardinality.Right;
            }
            BodyContact = kinect.Spine;
        }

        public enum HandCardinality
        {
            Left,
            Right
        }

        private GestureCardinality Cardinality;
        private TuioContact ThumbContact, IndexContact, MiddleContact, RingContact, PinkyContact, HandContact, WristContact, BodyContact;

        [Flags]
        private enum ActiveContacts
        {
            Thumb = 1,
            Index = 2,
            Middle = 4,
            Ring = 8,
            Pinky = 16
        }

        private const ActiveContacts ModeASlide = (ActiveContacts.Thumb | ActiveContacts.Index);
        private const ActiveContacts ModeADrag = (ActiveContacts.Index);
        private const ActiveContacts ModeBSlide = (ActiveContacts.Thumb | ActiveContacts.Index | ActiveContacts.Middle);
        private const ActiveContacts ModeBDrag = (ActiveContacts.Index | ActiveContacts.Middle);
        private const ActiveContacts ModeCSlide = (ActiveContacts.Thumb | ActiveContacts.Index | ActiveContacts.Middle | ActiveContacts.Ring | ActiveContacts.Pinky);
        private const ActiveContacts ModeCDrag = 0;
        private const ActiveContacts ModeDSlide = (ActiveContacts.Thumb | ActiveContacts.Index | ActiveContacts.Middle | ActiveContacts.Ring);

        private Type SlideAType = typeof(SlideA);
        private Type SlideBType = typeof(SlideB);
        private Type SlideCType = typeof(SlideC);
        private Type SlideDType = typeof(SlideD);
        private Type DragAType = typeof(DragA);
        private Type DragBType = typeof(DragB);
        private Type DragCType = typeof(DragC);

        private ActiveContacts ContactSet = 000000;

        private BasicGesture CurrentGesture = null;

        public override void ConstructGestures(ContactFrameReadyEventArgs frame)
        {
            ProcessNewContacts(frame.NewContacts);
            ProcessRemovedContacts(frame.RemovedContacts);

            if (ContactSet == ModeASlide)
            {
                if (!IsCurrentGestureValid<SlideA>(IndexContact))
                {
                    SlideA slide = new SlideA(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, ThumbContact, IndexContact, HandContact, Cardinality);
                    CurrentGesture = slide;
                }
            }

            else if (ContactSet == ModeADrag)
            {
                if (!IsCurrentGestureValid<DragA>(IndexContact))
                {
                    DragA drag = new DragA(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, IndexContact, HandContact, Cardinality);
                    CurrentGesture = drag;
                }
            }
            else if (ContactSet == ModeBSlide)
            {
                if (!IsCurrentGestureValid<SlideB>(IndexContact))
                {
                    SlideB slide = new SlideB(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, ThumbContact, IndexContact, MiddleContact, HandContact, Cardinality);
                    CurrentGesture = slide;
                }
            }
            else if (ContactSet == ModeBDrag)
            {
                if (!IsCurrentGestureValid<DragB>(IndexContact))
                {
                    DragB drag = new DragB(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, IndexContact, MiddleContact, HandContact, Cardinality);
                    CurrentGesture = drag;
                }
            }
            else if (ContactSet == ModeDSlide)
            {
                if (!IsCurrentGestureValid<SlideD>(IndexContact))
                {
                    SlideD slide = new SlideD(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, ThumbContact, IndexContact, MiddleContact, RingContact, HandContact, Cardinality);
                    CurrentGesture = slide;
                }
            }
            else if (ContactSet == ModeCSlide)
            {
                if (!IsCurrentGestureValid<SlideC>(IndexContact))
                {
                    SlideC slide = new SlideC(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, ThumbContact, IndexContact, MiddleContact, RingContact, PinkyContact, HandContact, Cardinality);
                    CurrentGesture = slide;
                }
            }
            else if (ContactSet == ModeCDrag)
            {
                bool crossesThreshold = BodyContact.zpos - HandContact.zpos > 200;
                if (CurrentGesture == null)
                {
                    if (crossesThreshold)
                    {
                        DragC drag = new DragC(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, HandContact, WristContact, Cardinality);
                        CurrentGesture = drag;
                    }
                }

                else if (crossesThreshold)
                {
                    if (CurrentGesture.GetType() == DragCType)
                        UpdateCurrentGesture(HandContact);
                    else
                    {
                        DestroyCurrentGesture(CurrentGesture.GetType());
                        DragC drag = new DragC(frame.Time, Recognizer.GetNextId(), frame.AdapterSource, HandContact, WristContact, Cardinality);
                        CurrentGesture = drag;
                    }
                }

                else
                {
                    DestroyCurrentGesture(CurrentGesture.GetType());
                    CurrentGesture = null;
                }
            }
        }

        private bool IsCurrentGestureValid<T>(TuioContact contact) where T : BasicGesture
        {
            if (CurrentGesture != null)
            {
                Type currentType = CurrentGesture.GetType();
                if (currentType == typeof(T))
                {
                    UpdateCurrentGesture(contact);
                    return true;
                }
                else
                    DestroyCurrentGesture(currentType);
            }
            return false;
        }

        private void UpdateCurrentGesture(TuioContact contact)
        {
            Vector3 currentPosition = CurrentGesture.CurrentPosition;
            if (contact.xpos != currentPosition.X &&
                contact.ypos != currentPosition.Y &&
                contact.zpos != currentPosition.Z)
                CurrentGesture.Update(contact);
        }

        private void DestroyCurrentGesture(Type currentType)
        {
            CurrentGesture.Destroy(DateTime.Now);
        }


        private void ProcessNewContacts(IEnumerable<TuioContact> newContacts)
        {
            foreach (TuioContact contact in newContacts)
            {
                if (contact == ThumbContact)
                    ContactSet |= ActiveContacts.Thumb;
                else if (contact == IndexContact)
                    ContactSet |= ActiveContacts.Index;
                else if (contact == MiddleContact)
                    ContactSet |= ActiveContacts.Middle;
                else if (contact == RingContact)
                    ContactSet |= ActiveContacts.Ring;
                else if (contact == PinkyContact)
                    ContactSet |= ActiveContacts.Pinky;
            }
        }

        private void ProcessRemovedContacts(IEnumerable<TuioContact> removedContacts)
        {
            foreach (TuioContact contact in removedContacts)
            {
                if (contact == ThumbContact)
                    ContactSet ^= ActiveContacts.Thumb;
                else if (contact == IndexContact)
                    ContactSet ^= ActiveContacts.Index;
                else if (contact == MiddleContact)
                    ContactSet ^= ActiveContacts.Middle;
                else if (contact == RingContact)
                    ContactSet ^= ActiveContacts.Ring;
                else if (contact == PinkyContact)
                    ContactSet ^= ActiveContacts.Pinky;
            }
        }
    }
}
